package com.example.passwordmanager.Util;


import org.apache.commons.codec.binary.Base32;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Base64;

/**
 * totp算法  用于2FA验证
 */
public class TOTP {


    private static final int DEFAULT_DIGITS = 6;
    private static final int DEFAULT_TIME_STEP = 30;

    public static int generateTOTP(String secretKey, long currentTimestamp) {
        return generateTOTP(secretKey, currentTimestamp, DEFAULT_DIGITS);
    }

    public static int generateTOTP(String secretKey, long currentTimestamp, int digits) {
        return generateTOTP(secretKey, currentTimestamp, digits, DEFAULT_TIME_STEP);
    }

    public static int generateTOTP(String secretKey, long currentTimestamp, int digits, int timeStep) {
        Base32 codec = new Base32();
        byte[] key =codec.decode(secretKey);

        long timeCounter = currentTimestamp / timeStep;

        byte[] counter = ByteBuffer.allocate(8).putLong(timeCounter).array();

        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA1");
        Mac mac;
        try {
            mac = Mac.getInstance("HmacSHA1");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Failed to get Mac instance", e);
        }
        try {
            mac.init(secretKeySpec);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("Failed to initialize Mac", e);
        }

        byte[] hmacResult = mac.doFinal(counter);

        int offset = hmacResult[hmacResult.length - 1] & 0x0f;

        int binary =
                ((hmacResult[offset] & 0x7f) << 24) |
                        ((hmacResult[offset + 1] & 0xff) << 16) |
                        ((hmacResult[offset + 2] & 0xff) << 8) |
                        (hmacResult[offset + 3] & 0xff);

        int otp = binary % (int) Math.pow(10, digits);

        return otp;
    }

}
